#include "installersdk.h"

#include "console.h"
//#include "constants.h"
#include "..\installer\constants.h"
#include "commandlineparser.h"
#include "installerbase.h"
#include "installerbasecommons.h"
#include "installerobjcommons.h"
#include "tabcontroller.h"
#include "mycontroller.h"

#include <binaryformatenginehandler.h>
#include <copydirectoryoperation.h>
#include <errors.h>
#include <init.h>
#include <updateoperations.h>
#include <messageboxhandler.h>
#include <packagemanagercore.h>
#include <packagemanagerproxyfactory.h>
#include <qprocesswrapper.h>
#include <protocol.h>
#include <productkeycheck.h>
#include <settings.h>
#include <utils.h>
#include <globals.h>

#include <runoncechecker.h>
#include <filedownloaderfactory.h>

#include <QDir>
#include <QDirIterator>
#include <QFontDatabase>
#include <QTemporaryFile>
#include <QTranslator>
#include <QUuid>
#include <QLoggingCategory>
#include <QScopedPointer>

#define USE_MYCONTROL

class installersdkPrivate {
public:
	installersdkPrivate()
		: m_core(Q_NULLPTR)
		, m_paramLoggingRules(QLatin1String("ifw.* = false")) // disable all by default
		, m_bParamProxy(false)
		, m_bParamShowVirtualComponents(false)
		, m_bParamUpdater(false)
		, m_bParamManagePackages(false)
		, m_bParamNoForceInstallation(false)
		, m_bParamCreateLocalRepository(false)
		, m_bParamSilentUpdate(false)
		, m_bMaintenanceTool(true)
	{
		
	}

	void registerMetaResources(const QInstaller::ResourceCollection &collection)
	{
		foreach(const QSharedPointer<QInstaller::Resource> &resource, collection.resources()) {
			const bool isOpen = resource->isOpen();
			if ((!isOpen) && (!resource->open()))
				continue;

			if (!resource->seek(0))
				continue;

			const QByteArray ba = resource->readAll();
			if (ba.isEmpty())
				continue;

			if (QResource::registerResource((const uchar*)ba.data(), QLatin1String(":/metadata")))
				m_resourceMappings.append(ba);

			if (!isOpen) // If we reach that point, either the resource was opened already...
				resource->close();           // or we did open it and have to close it again.
		}
	}
public:
	QInstaller::PackageManagerCore *m_core;

	QList<QByteArray> m_resourceMappings;
	QString m_strResourcePath;

	// params
	QString m_paramLoggingRules;
	QString m_paramStartClient;
	QString m_paramControlScript;
	bool m_bParamProxy;
	bool m_bParamShowVirtualComponents;
	bool m_bParamUpdater;
	bool m_bParamManagePackages;
	QString m_paramAddRepository;
	QString m_paramAddTmpRepository;
	QString m_paramSetTmpRepository;
	QString m_paramInstallCompressedRepository;
	bool m_bParamNoForceInstallation;
	bool m_bParamCreateLocalRepository;
	QStringList m_paramPositionalArguments;
	bool m_bParamSilentUpdate;
	bool m_bMaintenanceTool;

	QString m_strTargetDir;


	//
	QScopedPointer<RunOnceChecker> m_runCheck;
	QHash<QString, QString> m_params;
	QScopedPointer<Console> m_console;
	//QScopedPointer<TabController> m_controller;
	QScopedPointer<MyController> m_controller;
};

installersdk::installersdk(QObject* parent)
	: QObject(parent)
	, d(new installersdkPrivate)
{
	QInstaller::init(); // register custom operations
}

installersdk::~installersdk()
{
	foreach(const QByteArray &ba, d->m_resourceMappings)
		QResource::unregisterResource((const uchar*)ba.data(), QLatin1String(":/metadata"));
}

void installersdk::initConnect()
{
	connect(d->m_core, &QInstaller::PackageManagerCore::metaJobProgress, this, &installersdk::metaJobProgress);
	connect(d->m_core, &QInstaller::PackageManagerCore::metaJobTotalProgress, this, &installersdk::metaJobTotalProgress);
	connect(d->m_core, &QInstaller::PackageManagerCore::metaJobInfoMessage, this, &installersdk::metaJobInfoMessage);
}

QString installersdk::controlScript()
{
	QString strControlScript;
	//if (parser.isSet(QLatin1String(CommandLineOptions::Script))) {
	//controlScript = parser.value(QLatin1String(CommandLineOptions::Script));
	if (d->m_paramControlScript.size() > 0) {
		strControlScript = d->m_paramControlScript;
		if (!QFileInfo(strControlScript).exists())
			throw QInstaller::Error(QLatin1String("Script file does not exist."));
	}
	else if (!d->m_core->settings().controlScript().isEmpty()) {
		strControlScript = QLatin1String(":/metadata/installer-config/")
			+ d->m_core->settings().controlScript();
	}

	return strControlScript;
}

int installersdk::init()
{
	if(QInstaller::isVerbose())
		d->m_console.reset(new Console());

	d->m_runCheck.reset(new RunOnceChecker(QDir::tempPath()
		+ QLatin1Char('/')
		+ qApp->applicationName()
		+ QLatin1String("1234865.lock")));
	if (d->m_runCheck->isRunning(RunOnceChecker::ConditionFlag::Lockfile)) {
		// It is possible that two installers with the same name get executed
		// concurrently and thus try to access the same lock file. This causes
		// a warning to be shown (when verbose output is enabled) but let's
		// just silently ignore the fact that we could not create the lock file
		// and check the running processes.
		if (d->m_runCheck->isRunning(RunOnceChecker::ConditionFlag::ProcessList)) {
			QInstaller::MessageBoxHandler::information(0, QLatin1String("AlreadyRunning"),
				tr("Waiting for %1").arg(qAppName()),
				tr("Another %1 instance is already running. Wait "
					"until it finishes, close it, or restart your system.").arg(qAppName()));
			return EXIT_FAILURE;
		}
	}

	QString fileName = datFile(binaryFile());
	quint64 cookie = QInstaller::BinaryContent::MagicCookieDat;
	if (fileName.isEmpty()) {
		fileName = binaryFile();
		cookie = QInstaller::BinaryContent::MagicCookie;
	}

	QFile binary(fileName);
	QInstaller::openForRead(&binary);

	qint64 magicMarker;
	QInstaller::ResourceCollectionManager manager;
	QList<QInstaller::OperationBlob> oldOperations;
	QInstaller::BinaryContent::readBinaryContent(&binary, &oldOperations, &manager, &magicMarker,
		cookie);

	// Usually resources simply get mapped into memory and therefore the file does not need to be
	// kept open during application runtime. Though in case of offline installers we need to access
	// the appended binary content (packages etc.), so we close only in maintenance mode.
	if (magicMarker != QInstaller::BinaryContent::MagicInstallerMarker)
		binary.close();

	//CommandLineParser parser;
	//parser.parse(arguments());

	/*QString loggingRules(QLatin1String("ifw.* = false")); // disable all by default
	if (d->m_bVerbose) {
		loggingRules = QString(); // enable all in verbose mode
	if (parser.isSet(QLatin1String(CommandLineOptions::LoggingRules))) {
	loggingRules = parser.value(QLatin1String(CommandLineOptions::LoggingRules))
	.split(QLatin1Char(','), QString::SkipEmptyParts)
	.join(QLatin1Char('\n')); // take rules from command line
	}
	}*/
	QLoggingCategory::setFilterRules(d->m_paramLoggingRules);

	qCDebug(QInstaller::lcTranslations) << "Language:" << QLocale().uiLanguages()
		.value(0, QLatin1String("No UI language set")).toUtf8().constData();
	//qDebug().noquote() << "Arguments:" << arguments().join(QLatin1String(", "));

	d->registerMetaResources(manager.collectionByName("QResources"));
	//if (parser.isSet(QLatin1String(CommandLineOptions::StartClient))) {
	//	const QStringList arguments = parser.value(QLatin1String(CommandLineOptions::StartClient))
	if (d->m_paramStartClient.size() > 0) {
		const QStringList arguments = d->m_paramStartClient
			.split(QLatin1Char(','), QString::SkipEmptyParts);
		d->m_core = new QInstaller::PackageManagerCore(
			magicMarker, oldOperations,
			arguments.value(0, QLatin1String(QInstaller::Protocol::DefaultSocket)),
			arguments.value(1, QLatin1String(QInstaller::Protocol::DefaultAuthorizationKey)),
			QInstaller::Protocol::Mode::Debug);
	}
	else {
		QHash<QString, QString> params;
		params[QInstaller::scTargetDir] = d->m_strTargetDir;
		d->m_core = new QInstaller::PackageManagerCore(magicMarker, oldOperations,
			QUuid::createUuid().toString(), QUuid::createUuid().toString(), QInstaller::Protocol::Mode::Production, params);
	}

	{
		d->m_core->setInstallerPath(binaryFile());
		using namespace QInstaller;
		ProductKeyCheck::instance()->init(d->m_core);
		ProductKeyCheck::instance()->addPackagesFromXml(QLatin1String(":/metadata/Updates.xml"));
		BinaryFormatEngineHandler::instance()->registerResources(manager.collections());
	}

	dumpResourceTree();

	

	// From Qt5.8 onwards a separate command line option --proxy is not needed as system
	// proxy is used by default. If Qt is built with QT_USE_SYSTEM_PROXIES false
	// then system proxies are not used by default.
	//if ((parser.isSet(QLatin1String(CommandLineOptions::Proxy))
	if (d->m_bParamProxy
#if QT_VERSION > 0x050800
		|| QNetworkProxyFactory::usesSystemConfiguration()
#endif
		) /*&& !parser.isSet(QLatin1String(CommandLineOptions::NoProxy)))*/ {
		d->m_core->settings().setProxyType(QInstaller::Settings::SystemProxy);
		KDUpdater::FileDownloaderFactory::instance().setProxyFactory(d->m_core->proxyFactory());
	}

	//if (parser.isSet(QLatin1String(CommandLineOptions::ShowVirtualComponents))) {
	if (d->m_bParamShowVirtualComponents) {
		QFont f;
		f.setItalic(true);
		QInstaller::PackageManagerCore::setVirtualComponentsFont(f);
		QInstaller::PackageManagerCore::setVirtualComponentsVisible(true);
	}

	//if (parser.isSet(QLatin1String(CommandLineOptions::Updater))) {
	if (d->m_bParamUpdater) {
		if (d->m_core->isInstaller())
			throw QInstaller::Error(QLatin1String("Cannot start installer binary as updater."));
		d->m_core->setUpdater();
	}

	//if (parser.isSet(QLatin1String(CommandLineOptions::ManagePackages))) {
	if (d->m_bParamManagePackages) {
		if (d->m_core->isInstaller())
			throw QInstaller::Error(QLatin1String("Cannot start installer binary as package manager."));
		d->m_core->setPackageManager();
	}

	//if (parser.isSet(QLatin1String(CommandLineOptions::AddRepository))) {
	//const QStringList repoList = repositories(parser
	//	.value(QLatin1String(CommandLineOptions::AddRepository)));
	if (d->m_paramAddRepository.size() > 0) {
		const QStringList repoList = repositories(d->m_paramAddRepository);
		if (repoList.isEmpty())
			throw QInstaller::Error(QLatin1String("Empty repository list for option 'addRepository'."));
		d->m_core->addUserRepositories(repoList);
	}

	//if (parser.isSet(QLatin1String(CommandLineOptions::AddTmpRepository))) {
	//	const QStringList repoList = repositories(parser
	//		.value(QLatin1String(CommandLineOptions::AddTmpRepository)));
	if (d->m_paramAddTmpRepository.size() > 0) {
		const QStringList repoList = repositories(d->m_paramAddTmpRepository);
		if (repoList.isEmpty())
			throw QInstaller::Error(QLatin1String("Empty repository list for option 'addTempRepository'."));
		d->m_core->setTemporaryRepositories(repoList, false);
	}

	//if (parser.isSet(QLatin1String(CommandLineOptions::SetTmpRepository))) {
	//	const QStringList repoList = repositories(parser
	//		.value(QLatin1String(CommandLineOptions::SetTmpRepository)));
	if (d->m_paramSetTmpRepository.size() > 0) {
		const QStringList repoList = repositories(d->m_paramSetTmpRepository);
		if (repoList.isEmpty())
			throw QInstaller::Error(QLatin1String("Empty repository list for option 'setTempRepository'."));
		d->m_core->setTemporaryRepositories(repoList, true);
	}

	//if (parser.isSet(QLatin1String(CommandLineOptions::InstallCompressedRepository))) {
	//	const QStringList repoList = repositories(parser
	//		.value(QLatin1String(CommandLineOptions::InstallCompressedRepository)));
	if (d->m_paramInstallCompressedRepository.size() > 0) {
		const QStringList repoList = repositories(d->m_paramInstallCompressedRepository);
		if (repoList.isEmpty())
			throw QInstaller::Error(QLatin1String("Empty repository list for option 'installCompressedRepository'."));
		foreach(QString repository, repoList) {
			if (!QFileInfo::exists(repository)) {
				qDebug() << "The file " << repository << "does not exist.";
				return EXIT_FAILURE;
			}
		}
		d->m_core->setTemporaryRepositories(repoList, false, true);
	}

	//QInstaller::PackageManagerCore::setNoForceInstallation(parser
	//	.isSet(QLatin1String(CommandLineOptions::NoForceInstallation)));
	QInstaller::PackageManagerCore::setNoForceInstallation(d->m_bParamNoForceInstallation);
	//QInstaller::PackageManagerCore::setCreateLocalRepositoryFromBinary(parser
	//	.isSet(QLatin1String(CommandLineOptions::CreateLocalRepository))
	QInstaller::PackageManagerCore::setCreateLocalRepositoryFromBinary(d->m_bParamCreateLocalRepository
		|| d->m_core->settings().createLocalRepository());

	//QHash<QString, QString> params;
	//const QStringList positionalArguments = parser.positionalArguments();
	foreach(const QString &argument, d->m_paramPositionalArguments) {
		if (argument.contains(QLatin1Char('='))) {
			const QString name = argument.section(QLatin1Char('='), 0, 0);
			const QString value = argument.section(QLatin1Char('='), 1, 1);
			d->m_params.insert(name, value);
			d->m_core->setValue(name, value);
		}
	}

	const QString directory = QLatin1String(":/translations");
	const QStringList translations = d->m_core->settings().translations();

	if (translations.isEmpty()) {
		foreach(const QLocale locale, QLocale().uiLanguages()) {
			QScopedPointer<QTranslator> qtTranslator(new QTranslator(QCoreApplication::instance()));
			const bool qtLoaded = qtTranslator->load(locale, QLatin1String("qt"),
				QLatin1String("_"), directory);

			if (qtLoaded || locale.language() == QLocale::English) {
				if (qtLoaded)
					QCoreApplication::instance()->installTranslator(qtTranslator.take());

				QScopedPointer<QTranslator> ifwTranslator(new QTranslator(QCoreApplication::instance()));
				if (ifwTranslator->load(locale, QLatin1String("ifw"), QLatin1String("_"), directory))
					QCoreApplication::instance()->installTranslator(ifwTranslator.take());

				// To stop loading other translations it's sufficient that
				// qt was loaded successfully or we hit English as system language
				break;
			}
		}
	}
	else {
		foreach(const QString &translation, translations) {
			QScopedPointer<QTranslator> translator(new QTranslator(QCoreApplication::instance()));
			if (translator->load(translation, QLatin1String(":/translations")))
				QCoreApplication::instance()->installTranslator(translator.take());
		}
	}

	{
		QDirIterator fontIt(QStringLiteral(":/fonts"));
		while (fontIt.hasNext()) {
			const QString path = fontIt.next();
			qCDebug(QInstaller::lcResources) << "Registering custom font" << path;
			if (QFontDatabase::addApplicationFont(path) == -1)
				qWarning() << "Failed to register font!";
		}
	}

	return QInstaller::PackageManagerCore::Success;
}

int installersdk::start()
{
	//Do not show gui with --silentUpdate, instead update components silently
	//if (parser.isSet(QLatin1String(CommandLineOptions::SilentUpdate))) {
	if (d->m_bParamSilentUpdate) {
		if (d->m_core->isInstaller())
			throw QInstaller::Error(QLatin1String("Cannot start installer binary as updater."));
		const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance();
		if (!productKeyCheck->hasValidLicense())
			throw QInstaller::Error(QLatin1String("Silent update not allowed."));
		d->m_core->setUpdater();
		d->m_core->updateComponentsSilently();
	}
	else {
		//create the wizard GUI
		//TabController controller(0);
		//d->m_controller.reset(new TabController(0));
		d->m_controller.reset(new MyController(this, 0));
		d->m_controller->setManager(d->m_core);
		d->m_controller->setManagerParams(d->m_params);
		d->m_controller->setControlScript(this->controlScript());
		if (d->m_core->isInstaller()) {
			//d->m_controller->setGui(new InstallerGui(d->m_core));
			d->m_controller->setGui(new InstallerObj(d->m_core));
		}
		else {
			//d->m_controller->setGui(new MaintenanceGui(d->m_core));
			d->m_controller->setGui(new MaintenanceObj(d->m_core));
			//Start listening to setValue changes that newly installed components might have
			//connect(d->m_core, &QInstaller::PackageManagerCore::valueChanged, &controller,
			//	&TabController::updateManagerParams);
		}

		QInstaller::PackageManagerCore::Status status =
			QInstaller::PackageManagerCore::Status(d->m_controller->init());
		if (status != QInstaller::PackageManagerCore::Success)
			return status;

#ifdef ENABLE_SQUISH
		int squishPort = 11233;
		if (parser.isSet(QLatin1String(CommandLineOptions::SquishPort))) {
			squishPort = parser.value(QLatin1String(CommandLineOptions::SquishPort)).toInt();
		}
		if (squishPort != 0) {
			if (Squish::allowAttaching(squishPort))
				qDebug() << "Attaching to squish port " << squishPort << " succeeded";
			else
				qDebug() << "Attaching to squish failed.";
	}
		else {
			qWarning() << "Invalid squish port number: " << squishPort;
		}
#endif
		// exec()
		//return this->status();
	}
	return QInstaller::PackageManagerCore::Success;
}

int installersdk::exec()
{
	//QEventLoop loop;
	//const int result = loop.exec();
	const int result = QCoreApplication::instance()->exec();
	if (result != 0)
		return result;

	if (d->m_core->finishedWithSuccess())
		return QInstaller::PackageManagerCore::Success;

	return status();
}

int installersdk::status()
{
	QInstaller::PackageManagerCore::Status status = d->m_core->status();
	switch (status) {
	case QInstaller::PackageManagerCore::Success:
		return status;

	case QInstaller::PackageManagerCore::Canceled:
		return status;

	default:
		break;
	}
	return QInstaller::PackageManagerCore::Failure;
}

QString installersdk::program()
{
	const QString strProgram =
		d->m_core->replaceVariables(d->m_core->value(QInstaller::scRunProgram));

	return strProgram;
}

void installersdk::runProgram()
{
	const QString strProgram = program();

	if (strProgram.isEmpty())
		return;

	const QStringList args = d->m_core->replaceVariables(d->m_core
		->values(QInstaller::scRunProgramArguments));

	qDebug() << "starting" << strProgram << args;
	QProcess::startDetached(strProgram, args);
}

///////////////////////////////////////////////////////
void installersdk::setResourcePath(const QString& path)
{
	d->m_strResourcePath = path;
}
void installersdk::setLoggingRules(const QString& rules)
{
	d->m_paramLoggingRules = QLatin1String("ifw.* = false"); // disable all by default
	if (QInstaller::isVerbose()) {
		d->m_paramLoggingRules = QString(); // enable all in verbose mode
		if (rules.size() > 0) {
			d->m_paramLoggingRules = rules
				.split(QLatin1Char(','), QString::SkipEmptyParts)
				.join(QLatin1Char('\n')); // take rules from command line
		}
	}
}
void installersdk::setStartClient(const QString& strStartClient)
{
	d->m_paramStartClient = strStartClient;
}
void installersdk::setControlScript(const QString& strControlScript)
{
	d->m_paramControlScript = strControlScript;
}
void installersdk::setProxy(bool bProxy)
{
	d->m_bParamProxy = bProxy;
}
void installersdk::setShowVirtualComponents(bool bShowVirtualComponents)
{
	d->m_bParamShowVirtualComponents = bShowVirtualComponents;
}
void installersdk::setUpdater(bool bUpdater)
{
	d->m_bParamUpdater = bUpdater;
}
void installersdk::setManagePackages(bool bManagePackages)
{
	d->m_bParamManagePackages = bManagePackages;
}
void installersdk::setAddRepository(const QString& strAddRepository)
{
	d->m_paramAddRepository = strAddRepository;
}
void installersdk::setAddTmpRepository(const QString& strAddTmpRepository)
{
	d->m_paramAddTmpRepository = strAddTmpRepository;
}
void installersdk::setSetTmpRepository(const QString& strSetTmpRepository)
{
	d->m_paramSetTmpRepository = strSetTmpRepository;
}
void installersdk::setInstallCompressedRepository(const QString& strInstallCompressedRepository)
{
	d->m_paramInstallCompressedRepository = strInstallCompressedRepository;
}
void installersdk::setNoForceInstallation(bool bNoForceInstallation)
{
	d->m_bParamNoForceInstallation = bNoForceInstallation;
}
void installersdk::setCreateLocalRepository(bool bCreateLocalRepository)
{
	d->m_bParamCreateLocalRepository = bCreateLocalRepository;
}
void installersdk::setPositionalArguments(const QStringList& listPositionalArguments)
{
	d->m_paramPositionalArguments = listPositionalArguments;
}
void installersdk::setSilentUpdate(bool bSilentUpdate)
{
	d->m_bParamSilentUpdate = bSilentUpdate;
}
void installersdk::setMaintenanceTool(bool bMaintenanceTool)
{
	d->m_bMaintenanceTool = bMaintenanceTool;
}

void installersdk::setVerbose(bool bVerbose)
{
	QInstaller::setVerbose(bVerbose);
}

void installersdk::setTargetDir(const QString& strTargetDir)
{
	//if (d->m_core == Q_NULLPTR) return;
	//d->m_core->setValue(QInstaller::scTargetDir, strTargetDir);
	d->m_strTargetDir = strTargetDir;
}

QString installersdk::targetDir()
{
	return d->m_strTargetDir;
}

int installersdk::componentCount()
{
	if (d->m_core == Q_NULLPTR) return 0;
	return d->m_core->components(QInstaller::PackageManagerCore::ComponentType::Root).count();
}

///////////////////////////////////////////////////////
// -- private

void installersdk::dumpResourceTree() const
{
	qCDebug(QInstaller::lcResources) << "Resource tree:";
	QDirIterator it(QLatin1String(":/"), QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden,
		QDirIterator::Subdirectories);
	while (it.hasNext()) {
		if (it.next().startsWith(QLatin1String(":/qt-project.org")))
			continue;
		qCDebug(QInstaller::lcResources) << "    " << it.filePath().toUtf8().constData();
	}
}

QStringList installersdk::repositories(const QString &list) const
{
	const QStringList items = list.split(QLatin1Char(','), QString::SkipEmptyParts);
	foreach(const QString &item, items)
		qDebug().noquote() << "Adding custom repository:" << item;
	return items;
}

bool installersdk::notify(QObject *receiver, QEvent *event)
{
	if (QCoreApplication::instance() == 0) return false;
	try {
		return QCoreApplication::instance()->notify(receiver, event);
	}
	catch (std::exception &e) {
		qFatal("Exception thrown: %s", e.what());
	}
	catch (...) {
		qFatal("Unknown exception caught.");
	}
	return false;
}

/*!
Returns the installer / maintenance tool binary. In case of an installer this will be the
installer binary itself, which contains the binary layout and the binary content. In case
of an maintenance tool, it will return a binary that has just a binary layout append.

Note on OS X: For compatibility reason this function will return the a .dat file located
inside the resource folder in the application bundle, as on OS X the binary layout cannot
be appended to the actual installer / maintenance tool binary itself because of signing.
*/
QString installersdk::binaryFile() const
{
	if (d->m_strResourcePath.size() > 0) return d->m_strResourcePath;

	QString binaryFile = QCoreApplication::applicationFilePath();
#ifdef Q_OS_OSX
	// The installer binary on OSX does not contain the binary content, it's put into
	// the resources folder as separate file. Adjust the actual binary path. No error
	// checking here since we will fail later while reading the binary content.
	QDir resourcePath(QFileInfo(binaryFile).dir());
	resourcePath.cdUp();
	resourcePath.cd(QLatin1String("Resources"));
	return resourcePath.filePath(QLatin1String("installer.dat"));
#endif
	return binaryFile;
}

/*!
Returns the corresponding .dat file for a given installer / maintenance tool binary or an
empty string if it fails to find one.
*/
QString installersdk::datFile(const QString &binaryFile) const
{
	QFile file(binaryFile);
	QInstaller::openForRead(&file);
	const quint64 cookiePos = QInstaller::BinaryContent::findMagicCookie(&file,
		QInstaller::BinaryContent::MagicCookie);
	if (!file.seek(cookiePos - sizeof(qint64)))    // seek to read the marker
		return QString(); // ignore error, we will fail later

	const qint64 magicMarker = QInstaller::retrieveInt64(&file);
	if (magicMarker == QInstaller::BinaryContent::MagicUninstallerMarker) {
		QFileInfo fi(binaryFile);
		QString bundlePath;
		if (QInstaller::isInBundle(fi.absoluteFilePath(), &bundlePath))
			fi.setFile(bundlePath);
		return fi.absoluteDir().filePath(fi.baseName() + QLatin1String(".dat"));
	}
	return QString();
}

